#include <GameEngine/UnitTest/unit_test.h>

#include <GameEngine/Geometry/Surfaces/trgl3dfast.h>
#include <GameEngine/Geometry/Location/point3d.h>


#ifdef GAMEENGINE_NAMESPACES
namespace GameEngine {
#endif


//! Launch the unit tests on Trgl3dFast objects
/*!
 @return True if all unit tests on Trgl3dFast objects were
         succesful, false otherwise
 */
template <class T>
bool UnitTest::test_geometry_trgl3dfast_functions() {
	bool res = true;

	// Get number of bytes
	MemoryManager::BytesCount bytes = MemoryManager::instance()->bytes();

	// Create triangle
	GameEngine::Point3d<T> p1 = GameEngine::Point3d<T>(0,0,0);
	GameEngine::Point3d<T> p2 = GameEngine::Point3d<T>(1,0,0);
	GameEngine::Point3d<T> p3 = GameEngine::Point3d<T>(0,1,0);
	GameEngine::Trgl3dFast<T> trgl(p1,p2,p3);

	// Test point accessors
	T tolerance = (T)0.00001;
	{{
		res &= UNIT_TEST_ERROR( "[Trgl3dFast] Test p1()", ( p1.get_square_distance(trgl.p1()) < tolerance ) );
		res &= UNIT_TEST_ERROR( "[Trgl3dFast] Test p2()", ( p2.get_square_distance(trgl.p2()) < tolerance ) );
		res &= UNIT_TEST_ERROR( "[Trgl3dFast] Test p3()", ( p3.get_square_distance(trgl.p3()) < tolerance ) );
	}}

	// Test parametric point
	{{
		res &= UNIT_TEST_ERROR( "[Trgl3dFast] Test parametric point()", (
			GameEngine::Point3d<T>(0.5,0.5,0.0).get_square_distance(trgl.point( GameEngine::Coords2d<T>(0.5,0.5) )) < tolerance ) );
		res &= UNIT_TEST_ERROR( "[Trgl3dFast] Test parametric point()", (
			p1.get_square_distance(trgl.point( GameEngine::Coords2d<T>(0,0) )) < tolerance ) );
		res &= UNIT_TEST_ERROR( "[Trgl3dFast] Test parametric point()", (
			p2.get_square_distance(trgl.point( GameEngine::Coords2d<T>(1,0) )) < tolerance ) );
		res &= UNIT_TEST_ERROR( "[Trgl3dFast] Test parametric point()", (
			p3.get_square_distance(trgl.point( GameEngine::Coords2d<T>(0,1) )) < tolerance ) );
	}}

	// Test parametric coordinates
	{{
		GameEngine::Point3d<T> p0(0.5,0.5,0.0);
		GameEngine::Point3d<T> p1(0.0,1.0,0.0);
		GameEngine::Point3d<T> p2(1.0,0.0,0.0);
		GameEngine::Point3d<T> p3(1.0,1.0,0.0);
		GameEngine::Point3d<T> p4(0.0,0.0,0.0);
		GameEngine::Point3d<T> p5(1.0,1.0,1.0);
		GameEngine::Coords2d<T> c0 = trgl.coordinates(p0);
		GameEngine::Coords2d<T> c1 = trgl.coordinates(p1);
		GameEngine::Coords2d<T> c2 = trgl.coordinates(p2);
		GameEngine::Coords2d<T> c3 = trgl.coordinates(p3);
		GameEngine::Coords2d<T> c4 = trgl.coordinates(p4);
		GameEngine::Coords2d<T> c5 = trgl.coordinates(p5);
		res &= UNIT_TEST_ERROR( "[Trgl3dFast] Test parametric coordinates()", ( GameEngine::Point3d<T>(c0.u(),c0.v(),0.0).get_square_distance(p0) < tolerance ) );
		res &= UNIT_TEST_ERROR( "[Trgl3dFast] Test parametric coordinates()", ( GameEngine::Point3d<T>(c1.u(),c1.v(),0.0).get_square_distance(p1) < tolerance ) );
		res &= UNIT_TEST_ERROR( "[Trgl3dFast] Test parametric coordinates()", ( GameEngine::Point3d<T>(c2.u(),c2.v(),0.0).get_square_distance(p2) < tolerance ) );
		res &= UNIT_TEST_ERROR( "[Trgl3dFast] Test parametric coordinates()", ( GameEngine::Point3d<T>(c3.u(),c3.v(),0.0).get_square_distance(p3) < tolerance ) );
		res &= UNIT_TEST_ERROR( "[Trgl3dFast] Test parametric coordinates()", ( GameEngine::Point3d<T>(c4.u(),c4.v(),0.0).get_square_distance(p4) < tolerance ) );
		res &= UNIT_TEST_ERROR( "[Trgl3dFast] Test parametric coordinates()", ( GameEngine::Point3d<T>(c5.u(),c5.v(),0.0).get_square_distance(p3) < tolerance ) );
	}}

	// Test coplanar function
	{{
		res &= UNIT_TEST_ERROR( "[Trgl3dFast] Test coplanar()", ( trgl.coplanar(GameEngine::Point3d<T>(0.5,0.5,0.0)) == true ));
		res &= UNIT_TEST_ERROR( "[Trgl3dFast] Test coplanar()", ( trgl.coplanar(GameEngine::Point3d<T>(0.5,0.5,1.0)) == false ));
	}}

	// Test inside function
	{{
		res &= UNIT_TEST_ERROR( "[Trgl3dFast] Test inside()", ( trgl.inside(GameEngine::Point3d<T>(0.5,0.5,0.0), false) == true ));
		res &= UNIT_TEST_ERROR( "[Trgl3dFast] Test inside()", ( trgl.inside(GameEngine::Point3d<T>(0.5,0.5,0.0), true) == false ));
		res &= UNIT_TEST_ERROR( "[Trgl3dFast] Test inside()", ( trgl.inside(GameEngine::Point3d<T>(0.5,0.5,1.0), false) == false ));
		res &= UNIT_TEST_ERROR( "[Trgl3dFast] Test inside()", ( trgl.inside(GameEngine::Point3d<T>(0.5,1.5,0.0), false) == false ));
	}}

	// Test intersection function
	{{
		GameEngine::Line3d<T> line(GameEngine::Point3d<T>(0.5,0.5,-1.0), GameEngine::Point3d<T>(0.5,0.5,1.0));
		T line_coordinate;
		GameEngine::Coords2d<T> coordinates;
		res &= UNIT_TEST_ERROR( "[Trgl3dFast] Test intersection()", ( trgl.intersection(line, line_coordinate, coordinates) == true ));
		res &= UNIT_TEST_ERROR( "[Trgl3dFast] Test intersection()", ( line_coordinate == 0.5 ));
		res &= UNIT_TEST_ERROR( "[Trgl3dFast] Test intersection()", ( coordinates.u() == 0.5 ));
		res &= UNIT_TEST_ERROR( "[Trgl3dFast] Test intersection()", ( coordinates.v() == 0.5 ));
	}}

	// Test closest_point function
	{{
		GameEngine::Coords2d<T> coords;
		res &= UNIT_TEST_ERROR( "[Trgl3dFast] Test closest_point()", ( trgl.closest_point(GameEngine::Point3d<T>(0.5,0.5,-1.0), coords) == true ));
		res &= UNIT_TEST_ERROR( "[Trgl3dFast] Test closest_point()", ( coords.u() == 0.5 ));
		res &= UNIT_TEST_ERROR( "[Trgl3dFast] Test closest_point()", ( coords.v() == 0.5 ));
		GameEngine::Point3d<T> pt = trgl.closest_point(GameEngine::Point3d<T>(0.5,0.5,-1.0));
		res &= UNIT_TEST_ERROR( "[Trgl3dFast] Test closest_point()", ( pt.x() == 0.5 ));
		res &= UNIT_TEST_ERROR( "[Trgl3dFast] Test closest_point()", ( pt.y() == 0.5 ));
		res &= UNIT_TEST_ERROR( "[Trgl3dFast] Test closest_point()", ( pt.z() == 0.0 ));
	}}

	// Test perimeter function
	{{
		res &= UNIT_TEST_ERROR( "[Trgl3dFast] Test perimeter()", ( abs(trgl.perimeter() - ( (T)2.0 + sqrt((T)2.0) )) < tolerance ));
	}}

	// Test area function
	{{
		res &= UNIT_TEST_ERROR( "[Trgl3dFast] Test area()", ( abs(trgl.area()-(T)0.5) < tolerance ));
	}}

	// Test degenerate function
	{{
		res &= UNIT_TEST_ERROR( "[Trgl3dFast] Test degenerate()", ( trgl.degenerate() == false ));
		res &= UNIT_TEST_ERROR( "[Trgl3dFast] Test degenerate()", ( GameEngine::Trgl3dFast<T>(p1,p2,p2).degenerate() == true ));
	}}

	// Get number of bytes
	res &= UNIT_TEST_ERROR( "[Trgl3dFast] Test memory leak", ( bytes == MemoryManager::instance()->bytes() ));

	// Return result
	return res;
}


#ifdef GAMEENGINE_NAMESPACES
} // namespace GameEngine
#endif
